#!/usr/bin/env ruby
#Protocol:
#http://www.teamfair.info/DC-Protocol.htm

require 'basiclib.rb'
require 'socket'
require 'iconv'
require 'plugin'
require 'yaml'
class DCHub < Plugin

	def initialize(coreURI,name,settings=nil)
		super(coreURI,name)
		if settings==nil
			@settings=Hash.new
		else
			@settings=settings
		end
		@settings[:hubhost]="dc.hovel.ru" unless @settings.has_key? :hubhost
		@settings[:hubport]=411 unless @settings.has_key? :hubport
		@settings[:user]=DCUser.new() unless @settings.has_key? :user

		@connected=false

		@Supports="NoHello NoGetINFO"

		@settings[:pass]=nil unless @settings.has_key? :pass
		@settings[:hubcharset]='cp1251' unless @settings.has_key? :hubcharset
		@settings[:outcharset]='utf-8' unless @settings.has_key? :outcharset
		
		@settings[:retryTimeout]=10 unless @settings.has_key? :retryTimeout
		@settings[:retryConnect]=true unless @settings.has_key? :retryConnect
		@settings[:myInfoInterval]=30 unless @settings.has_key? :myInfoInterval

		@settings[:active_port]=1411 unless @settings.has_key? :actvie_port
		@settings[:mode]=:passive unless @settings.has_key? :passive
		
		@settings[:myhost]="192.168.1.1" unless @settings.has_key? :myhost

		@timer=nil
		@userList=Hash.new
		
		DRb.thread.join
	end

	def connectToHub
		emit(:debugmsg,"connecting to hub...")
                while not @connected
                        begin
				emit(:debugmsg, "Connecting to #{@settings[:hubhost]}:#{@settings[:hubport]}")
				emit(:connecting)
                                @hubStream = TCPSocket::new(@settings[:hubhost],@settings[:hubport])
				@connected=true
                        rescue
                                emit(:debugmsg, "CONNECTION ERROR: " + $!)
				emit(:disconnected,$!)
				if (@settings[:retryConnect])
					emit(:debugmsg,"Reconnecting in #{@settings[:retryTimeout]} seconds")
					sleep @settings[:retryTimeout]
                                	retry
				end
                                @connected=false
                        end
                end

	end
	
	def runme
		Thread.new{run}
	end
	
	def run	
		emit(:debugmsg,"running hub")
		connectToHub
		@timer=Thread.new{
				while true
					if @connected
						sleep 30
						sendMyInfo
					end
				end
			}
		while @connected
			res=select([@hubStream],nil,nil,nil)
			for sock in res[0]
				if sock.eof?
					emit(:debugmsg, "Connection Lost!")
					emit(:lostconnect)
					@connected=false
					if (@settings[:retryConnect])
						sleep @settings[:retryTimeout]
						connectToHub
					end
					break
				else
					m=""
					while m[-1]!="|"[0]
						 m=m+Iconv.iconv(@settings[:outcharset],@settings[:hubcharset],sock.readpartial(4096).to_s).to_s
					end
					hub_recived(m)
				end
			end
		end
	end
	

	private
	def hub_recived (messages)
		#splitting message
		messages.split("|").each {|msg|
			if msg!=nil
				if msg=~/^\$/
					hub_sys_msg(msg)
				else
					hub_chat_msg(msg)
				end
			end
		}
	end
	
	def hub_sys_msg (msg)
		ar=msg.split(" ",2)
		case ar[0]
			when "$Lock"
				emit(:debugmsg, "Lock recived")
				lock=msg.match(/\$Lock[\s](.*?)[\s]/).to_s
				key=lock2key(lock)
                        	@hubStream.write("$Supports #{@Supports}|$Key #{key} |$ValidateNick #{@settings[:user].nick}|")
                        	if @settings[:pass]==nil
                                	pmsg=""
                        	else
                                	pmsg="|$MyPass #{@settings[:pass]}"
				end
                        	sendToHub("$Version 1,0091#{pmsg}|#{@settings[:user].myInfo}|")
			
			when "$Hello"
				if ar[1]==@settings[:user].nick
					sendToHub("$GetNickList|")
					emit(:debugmsg, "connected successful")
					emit(:connected)
				else
					emit(:debugmsg, "Hello user #{ar[1]}")
					sendToHub("$GetINFO #{ar[1]} #{@settings[:user].nick}|")
				end

			when "$Supports"
				@hubSupports=ar[1].split(" ")
				emit(:debugmsg, "supports recived #{@hubSupports.to_s}")

			when "$HubIsFull"
				emit(:debugmsg,"Hub Is Full")
				emit(:hubIsFull)

			when "$HubName"
				emit(:debugmsg,"HubName recived")
				@selfname=ar[1]
			when "$NickList"
				ar[1].split("$$").each {|nick| sendToHub("$GetINFO #{nick} #{@settings[:user].nick}|")}
				emit(:debugmsg, "NickList recived.")
				emit(:userListRecived)

			when "$OpList"
				@opList=ar[1].split("$$")
				@opList.each {|nick| sendToHub("$GetINFO #{nick} #{@settings[:user].nick}|")}
				emit(:debugmsg,"OpList recived. ")
				emit(:opListRecived,@opList)
			when "$HubTopic"
				@hubTopic=ar[1]
				emit(:debugmsg, "HubTopic recived. Topic: #{ar[1]}")
				emit(:hubTopicRecived,ar[1])
			when "$ValidateDenie"
				emit(:debugmsg,"Err nick")
				emit(:nickAlreadyTaken)
			when "$GetPass"
				sendToHub("$MyPass #{@settings[:pass]}|")
				emit(:sendPasswd)
				emit(:debugmsg,"Sending Passwd")
			when "$BadPass"
				emit(:badPaswd)
				emit(:debugmsg,"Bad Passwd")

			when "$LogedIn"
				emit(:loggedIn)
				emit(:debugmsg,"LoggedIn")

			when "$MyINFO"
				#emit(:debugmsg,"myinfo recived: #{msg}")
				user=DCUser.new(msg)
				if (@userList.has_key?(user.nick))
					emit(:debugmsg,"Myinfo updated by #{user.nick} #{msg}")
					emit(:myInfoUpdated,user)
				else
					emit(:debugmsg,"User Joined: #{user.nick} usercount: #{@userList.size} myinfo #{msg}")
					emit(:newUser,user)
				end
				@userList[user.nick]=user
			when "$Search"
				emit(:searchReq,msg)

			when "$Quit"
				@userList.delete(ar[1])
				emit(:debugmsg,"user left: #{ar[1]}")
				emit(:userLeft,ar[1])
			when "$SR"
				emit(:debugmsg,"search result #{ar[1]}")
				emit(:searchResult,ar[1])
			else
				emit(:debugmsg, "Unknown system message: #{msg}")
		end
	end
	def hub_chat_msg (msg)
		emit(:chatmsg, msg)
	end

	public
	def chatToHub(msg)
		sendToHub("<#{@settings[:user].nick}> #{msg}|")
	end
	def sendToHub(msg)
		emit(:debugmsg,"sending: #{msg}")
		begin 
			m=Iconv.iconv(@settings[:hubcharset],@settings[:outcharset],msg).to_s
			@hubStream.write(m)
		rescue Iconv::IllegalSequence
			emit(:debugmsg, "iconv error!")
		end

	end
	
	def sendMyInfo
		sendToHub("#{@settings[:user].myInfo}|")
	end

		
		attr_accessor :settings
		attr_reader :connected
		attr_reader :userList
end #dchub


if __FILE__ == $0

	def out(msg)
		puts (msg)
	end
	dc=DCHub.new("druby://localhost:1555","megabot","test","dc.hovel.ru",411)
	connect(dc,:debugmsg,self,:out)
	connect(dc,:chatmsg,self,:out)
	dc.run

end
